---
title: 面向對象程式設計基礎
---

面向對象程式設計 (OOP) 是一種基於「物件」概念的程式設計範式，物件可以包含資料和程式碼。C++ 是一種多範式語言，對 OOP 有強大的支援，而 JavaScript 使用基於原型的物件模型。

## 類別和物件定義

在 C++ 中，**類別**是建立物件的藍圖，為狀態（成員變數）提供初始值，並實作行為（成員函式）。**物件**是類別的一個實例。

<UniversalEditor title="類別和物件定義比較" compare={true}>
```javascript !! js
// JavaScript: 物件字面量和建構函式
// 物件字面量
const car1 = {
  brand: "Toyota",
  model: "Camry",
  start: function() {
    console.log(`${this.brand} ${this.model} 啟動了。`);
  }
};
car1.start();

// 建構函式
function Car(brand, model) {
  this.brand = brand;
  this.model = model;
  this.start = function() {
    console.log(`${this.brand} ${this.model} 啟動了。`);
  };
}
const car2 = new Car("Honda", "Civic");
car2.start();

// ES6 類別語法 (原型上的語法糖)
class Vehicle {
  constructor(brand, model) {
    this.brand = brand;
    this.model = model;
  }
  start() {
    console.log(`${this.brand} ${this.model} 啟動了。`);
  }
}
const car3 = new Vehicle("Ford", "Focus");
car3.start();
```

```cpp !! cpp
// C++: 類別和物件定義
#include <iostream>
#include <string>

class Car { // 類別定義
public: // 存取修飾符
  std::string brand; // 成員變數
  std::string model;

  void start() { // 成員函式
    // std::cout << brand << " " << model << " 啟動了。" << std::endl;
  }
};

int main() {
  Car car1; // 建立 Car 類別的物件 (實例)
  car1.brand = "Toyota";
  car1.model = "Camry";
  car1.start();

  Car car2; // 另一個物件
  car2.brand = "Honda";
  car2.model = "Civic";
  car2.start();

  return 0;
}
```
</UniversalEditor>

## 建構函式和解構函式

**建構函式**是物件建立時自動呼叫的特殊成員函式。它們用於初始化物件的狀態。

**解構函式**是物件銷毀時（超出作用域或被 `delete`）自動呼叫的特殊成員函式。它們用於清理資源（例如，釋放動態分配的記憶體）。

<UniversalEditor title="建構函式和解構函式比較" compare={true}>
```javascript !! js
// JavaScript: 建構函式和類別建構函式
function Person(name, age) {
  this.name = name;
  this.age = age;
  console.log(`${this.name} 已建立。`);
}
const p1 = new Person("Alice", 30);
// 沒有明確的解構函式；垃圾回收器處理清理

class Animal {
  constructor(name) {
    this.name = name;
    console.log(`${this.name} (Animal) 已建立。`);
  }
  // 沒有明確的解構函式
}
const a1 = new Animal("Leo");
```

```cpp !! cpp
// C++: 建構函式和解構函式
#include <iostream>
#include <string>

class Person {
public:
  std::string name;
  int age;

  // 建構函式
  Person(std::string n, int a) : name(n), age(a) {
    // std::cout << name << " (Person) 已建立。" << std::endl;
  }

  // 解構函式
  ~Person() {
    // std::cout << name << " (Person) 已銷毀。" << std::endl;
  }
};

int main() {
  Person p1("Alice", 30); // 建構函式被呼叫
  // p1 在此超出作用域，解構函式自動被呼叫

  Person* p2 = new Person("Bob", 25); // 建構函式被呼叫
  delete p2; // 解構函式明確被呼叫

  return 0;
}
```
</UniversalEditor>

## 封裝和存取控制

**封裝**是將資料（成員變數）和操作資料的方法（成員函式）捆綁到一個單元（類別）中，並限制對物件某些組件的直接存取。這透過**存取修飾符**實現：

*   `public`：成員可從任何地方存取。
*   `private`：成員只能從類別內部存取。
*   `protected`：成員可從類別內部和衍生類別存取。

<UniversalEditor title="封裝比較" compare={true}>
```javascript !! js
// JavaScript: 封裝 (使用閉包或 #private 欄位)
class BankAccount {
  #balance; // 私有欄位 (ES2019+)

  constructor(initialBalance) {
    this.#balance = initialBalance;
  }

  deposit(amount) {
    if (amount > 0) {
      this.#balance += amount;
      console.log(`已存入: ${amount}。新餘額: ${this.#balance}`);
    }
  }

  getBalance() {
    return this.#balance;
  }
}

const myAccount = new BankAccount(100);
myAccount.deposit(50);
// console.log(myAccount.#balance); // SyntaxError: 私有欄位 '#balance' 必須在封閉類別中宣告
console.log(myAccount.getBalance()); // 150
```

```cpp !! cpp
// C++: 帶有存取修飾符的封裝
#include <iostream>

class BankAccount {
private: // 私有成員變數
  double balance;

public: // 公有成員函式
  BankAccount(double initialBalance) : balance(initialBalance) {}

  void deposit(double amount) {
    if (amount > 0) {
      balance += amount;
      // std::cout << "已存入: " << amount << ". 新餘額: " << balance << std::endl;
    }
  }

  double getBalance() const { // const 表示它不修改物件狀態
    return balance;
  }
};

int main() {
  BankAccount myAccount(100);
  myAccount.deposit(50);
  // myAccount.balance = 1000; // 錯誤: 'balance' 是私有的
  // std::cout << myAccount.getBalance() << std::endl; // 150
  return 0;
}
```
</UniversalEditor>

## 基本繼承

**繼承**是一種機制，其中從現有類別（基底類別或超類別）建立新類別（衍生類別或子類別）。衍生類別繼承基底類別的屬性和行為，促進程式碼重用。

<UniversalEditor title="基本繼承比較" compare={true}>
```javascript !! js
// JavaScript: 類別繼承
class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(`${this.name} 發出聲音。`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 呼叫父類別建構函式
    this.breed = breed;
  }
  speak() {
    console.log(`${this.name} (${this.breed}) 吠叫。`);
  }
}

const myDog = new Dog("Buddy", "黃金獵犬");
myDog.speak(); // Buddy (黃金獵犬) 吠叫。
```

```cpp !! cpp
// C++: 基本繼承
#include <iostream>
#include <string>

class Animal { // 基底類別
public:
  std::string name;
  Animal(std::string n) : name(n) {}
  void speak() {
    // std::cout << name << " 發出聲音。" << std::endl;
  }
};

class Dog : public Animal { // 衍生類別，公有繼承
public:
  std::string breed;
  Dog(std::string n, std::string b) : Animal(n), breed(b) {} // 呼叫基底類別建構函式
  void speak() { // 覆寫基底類別方法
    // std::cout << name << " (" << breed << ") 吠叫。" << std::endl;
  }
};

int main() {
  Dog myDog("Buddy", "黃金獵犬");
  myDog.speak();
  return 0;
}
```
</UniversalEditor>

## 多型概念

**多型**（意為「多種形式」）允許不同類別的物件被視為共同基底類別的物件。在 C++ 中，多型主要透過**虛擬函式**和**指向基底類別的指標/引用**實現。

<UniversalEditor title="多型概念比較" compare={true}>
```javascript !! js
// JavaScript: 多型 (透過動態類型和原型自然實現)
class Shape {
  draw() {
    console.log("繪製一個通用形狀。");
  }
}

class Circle extends Shape {
  draw() {
    console.log("繪製一個圓形。");
  }
}

class Rectangle extends Shape {
  draw() {
    console.log("繪製一個矩形。");
  }
}

const shapes = [new Shape(), new Circle(), new Rectangle()];

shapes.forEach(shape => shape.draw());
// 輸出:
// 繪製一個通用形狀。
// 繪製一個圓形。
// 繪製一個矩形。
```

```cpp !! cpp
// C++: 帶有虛擬函式的多型
#include <iostream>

class Shape { // 基底類別
public:
  virtual void draw() { // 虛擬函式
    // std::cout << "繪製一個通用形狀。" << std::endl;
  }
};

class Circle : public Shape { // 衍生類別
public:
  void draw() override { // override 關鍵字 (C++11) 用於清晰度
    // std::cout << "繪製一個圓形。" << std::endl;
  }
};

class Rectangle : public Shape { // 衍生類別
public:
  void draw() override {
    // std::cout << "繪製一個矩形。" << std::endl;
  }
};

int main() {
  Shape* s1 = new Shape();
  Shape* s2 = new Circle(); // 指向衍生類別物件的基底類別指標
  Shape* s3 = new Rectangle();

  s1->draw(); // 呼叫 Shape::draw()
  s2->draw(); // 呼叫 Circle::draw() (多型行為)
  s3->draw(); // 呼叫 Rectangle::draw() (多型行為)

  delete s1; delete s2; delete s3;
  return 0;
}
```
</UniversalEditor>

## 與 JavaScript 原型鏈的比較

JavaScript 的物件模型是基於原型的，而不是傳統意義上的基於類別（儘管 ES6 類別提供了語法糖）。當你嘗試存取物件上的屬性或方法時，JavaScript 會首先直接在物件上尋找。如果找不到，它會在物件的原型上尋找，然後在原型的原型上尋找，依此類推，形成一個**原型鏈**。

**主要差異：**
*   **繼承模型：** C++ 使用經典繼承（基於類別），而 JavaScript 使用原型繼承。
*   **明確 vs. 隱含：** C++ 繼承是明確的，帶有 `class` 和 `public/private/protected` 等關鍵字。JavaScript 的原型繼承更為隱含。
*   **多型：** C++ 依賴虛擬函式和基底類別指標/引用來實現運行時多型。JavaScript 由於其動態特性和原型鏈而自然實現多型。

## 虛擬函式機制

在 C++ 中，**虛擬函式**是在基底類別中宣告並由衍生類別重新定義（覆寫）的成員函式。當你透過指向基底類別的指標或引用呼叫虛擬函式時，執行哪個特定版本的函式是在運行時根據所指向或引用的物件的實際類型確定的。這稱為**動態分派**或**運行時多型**。

*   **`virtual` 關鍵字：** 在基底類別中將函式宣告為虛擬函式。
*   **`override` 關鍵字 (C++11)：** 可選，但在衍生類別中是個好習慣，用於指示函式旨在覆寫基底類別中的虛擬函式。如果簽名不匹配，有助於捕獲錯誤。
*   **虛擬表 (vtable)：** 編譯器通常使用虛擬表來實作虛擬函式，虛擬表是函式指標的查找表。

---

### 練習題：
1.  解釋 C++ 中封裝和繼承的概念。存取修飾符 (`public`、`private`、`protected`) 如何與封裝相關？
2.  C++ 中的多型是什麼，以及如何使用虛擬函式實現它？提供一個簡單的 C++ 程式碼範例，演示運行時多型。
3.  比較和對比 C++ 的基於類別的繼承與 JavaScript 的基於原型的繼承。它們在實現面向對象原則方面的主要差異是什麼？

### 專案構想：
*   使用繼承和多型在 C++ 中設計並實作一個簡單的 `Shape` 階層。建立帶有虛擬 `calculateArea()` 方法的基底類別 `Shape`。從 `Shape` 衍生 `Circle` 和 `Rectangle` 類別，每個類別都覆寫 `calculateArea()` 以提供其特定的實作。然後，建立一個 `Shape` 指標陣列，並演示對不同形狀多型地呼叫 `calculateArea()`。
